﻿using AiMoWindows.GHAiMo;
using AiMoWindows.Localization;
using AiMoWindows.MainActions.Mode;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace AiMoWindows.CodeScripts.Analysis
{
    static class SyntaxConverter
    {
        /// <summary>
        /// "Main"
        /// </summary>
        public static string MainMethodName_RhinoScript = "Main";
        /// <summary>
        /// "SolveInstance"
        /// </summary>
        public static string MainMethodName_GrasshopperScript = "SolveInstance";
        /// <summary>
        /// "CSharpScriptInstance"
        /// </summary>
        public static string Script_InstanceName_RhinoScript = "CSharpScriptInstance";
        /// <summary>
        /// "GHCSharpScriptInstance"
        /// </summary>
        public static string Script_InstanceName_GrasshopperScript = "GHCSharpScriptInstance";
        public static SyntaxConvertResult ConvertDocumentToAiMoScriptSource(SyntaxNode? rootnode)
        {
            SyntaxConvertResult result = new SyntaxConvertResult();

            if (rootnode is CompilationUnitSyntax compilation)
            {
                SourceText sourceText = compilation.GetText();

                if (!AnalysisRootMember(compilation, sourceText, result))
                {
                    return result;
                }
                UsingFix.PerformFix(compilation, result);
                result.ReferenceDirectives = FixReferenceDirectives(compilation, out string printInfo);
                result.PrintInformation += printInfo;
            }
            else
            {
                result.PrintInformation = "Cannot Get CompilationUnitSyntax in SyntaxNode when convert document to AiMo Script Source";
                return result;
            }

            if (result.CompileMode == ScriptComplileMode.GrasshopperScript)
            {
                result.GH_ParameterInfo = GH_SyntaxAnalyzer.FindMethodCalls(rootnode, out string printInfo, out bool canSendToGH);
                result.PrintInformation += printInfo;
                if (!canSendToGH) return result;
            }

            result.Result = true;
            return result;
        }

        static string[] FixReferenceDirectives(CompilationUnitSyntax compilation, out string printInfo)
        {
            List<string> references = new List<string>();
            printInfo = "";
            // Returns #r directives specified in the compilation.
            foreach (var directive in compilation.GetReferenceDirectives())
            {
                string filePath = directive.File.ValueText;
                if (!System.IO.Path.IsPathFullyQualified(filePath))
                {
                    printInfo += string.Format(Local.Get("Reference file (#r {0}) is not fully file path."), filePath);
                }
                else if (!System.IO.File.Exists(filePath))
                {
                    printInfo += string.Format(Local.Get("Reference file (#r {0}) is not existed."), filePath);
                }
                references.Add(filePath);
            }
            return references.ToArray();
        }




        static bool AnalysisRootMember(CompilationUnitSyntax compilation, SourceText sourceText, SyntaxConvertResult result)
        {
            //默认为External Assemblies
            result.CompileMode = ScriptComplileMode.ExternalAssemblies;

            // 处理一级子member，可能是namespace或者直接定义的class/enum/struct等等
            foreach (MemberDeclarationSyntax member in compilation.Members)
            {
                if (member is FileScopedNamespaceDeclarationSyntax namespaceDeclarationSyntax)
                {
                    result.HasNameSpace = true;
                    result.Classes.Add($"namespace {namespaceDeclarationSyntax.Name};");
                    foreach (MemberDeclarationSyntax subMember in namespaceDeclarationSyntax.Members)
                    {
                        if (!DealWithFirstLevelClass(subMember, sourceText, result)) return false;
                    }
                }
                else if (member is NamespaceDeclarationSyntax namespaceSyntax)
                {
                    result.HasNameSpace = true;
                    result.Classes.Add($"namespace {namespaceSyntax.Name};");
                    foreach (MemberDeclarationSyntax subMember in namespaceSyntax.Members)
                    {
                        if (!DealWithFirstLevelClass(subMember, sourceText, result)) return false;
                    }
                }
                else
                {
                    if (!DealWithFirstLevelClass(member, sourceText, result)) return false;
                }
            }
            //处理NameSpace的关系
            if (result.CompileMode != ScriptComplileMode.ExternalAssemblies && result.HasNameSpace)
            {
                // 不应该出现 namespace
                result.PrintInformation = result.CompileMode == ScriptComplileMode.RhinoScript ?
                    Local.Get("Cannot set namespace when compile code for AiMo Rhino") :
                    Local.Get("Cannot set namespace when compile code for AiMo Grasshopper");
                return false;
            }
            else if (result.CompileMode == ScriptComplileMode.ExternalAssemblies && !result.HasNameSpace)
            {
                Local.Get("Warning! It is recommended to set a namespace when compiling code from external assemblies.");
            }

            return true;
        }
        /// <summary> 
        /// 运行这个函数，处理输入的一级代码类
        /// 如果包含正确的
        /// </summary>
        static bool DealWithFirstLevelClass(MemberDeclarationSyntax member, SourceText sourceText, SyntaxConvertResult result)
        {
            // 当前的member是ScriptInstace的Class
            MainClassType mainClassType = IsMainClass(member, out ClassDeclarationSyntax? classDeclaration);

            if (mainClassType == MainClassType.None || classDeclaration == null)
            {
                // 处理其他Class
                result.Classes.AddLinesFromSourceText(member.FullSpan, sourceText);
                if (member.Modifiers.Any(x => x.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.PublicKeyword)))
                {
                    result.HasPublicCustomizedClasses = true;
                }
            }
            else
            {
                // Found : CSharpScriptInstance
                // Deal with Main method
                int mainMethodCount = 0;
                MainMethodType mainMethodType = MainMethodType.None;
                foreach (MemberDeclarationSyntax mainClassMember in classDeclaration.Members)
                {
                    mainMethodType = mainClassMember.IsMainMethod(out TextSpan methodBodySpan);
                    if (mainMethodType != MainMethodType.None)
                    {
                        // main method的前缀不对，需要自动修复
                        mainMethodCount++;

                        // Found Main Method
                        result.Mains.AddLinesFromSourceText(methodBodySpan, sourceText);
                        // 处理其他部分Span

                        TextSpan beforeMainContent = TextSpan.FromBounds(
                            classDeclaration.OpenBraceToken.Span.End,
                            mainClassMember.FullSpan.Start
                            );
                        result.Functions.AddLinesFromSourceText(beforeMainContent, sourceText);

                        TextSpan afterMainContent = TextSpan.FromBounds(
                            mainClassMember.FullSpan.End,
                            classDeclaration.CloseBraceToken.Span.Start
                            );
                        result.Functions.AddLinesFromSourceText(afterMainContent, sourceText);
                        break;
                    }
                }

                if (mainMethodCount != 1)
                {
                    // 有多余的Main方法
                    result.PrintInformation = result.CompileMode == ScriptComplileMode.RhinoScript ?
                        Local.Get("Must contains only one Main() method in the CSharpScriptInstance class") :
                        Local.Get("Must contains only one SolveInstance() method in the GHCSharpScriptInstance class");
                    return false;
                }
                else
                {
                    if ((int)mainClassType != (int)mainMethodType)
                    {
                        // 类名和方法名称不匹配
                        result.PrintInformation = mainClassType == MainClassType.RhinoInstance ?
                            Local.Get("Must contains only one Main() method in the CSharpScriptInstance class") :
                            Local.Get("Must contains only one SolveInstance() method in the GHCSharpScriptInstance class");
                        return false;
                    }
                    result.CompileMode = mainMethodType == MainMethodType.Main ? ScriptComplileMode.RhinoScript : ScriptComplileMode.GrasshopperScript;
                    return true;
                }
            }



            return true;
        }


        static MainMethodType IsMainMethod(this MemberDeclarationSyntax member, out TextSpan methodBodySpan)
        {
            MainMethodType mainMethodName = MainMethodType.None;
            if (member is MethodDeclarationSyntax methodDeclaration)
            {
                string name = methodDeclaration.Identifier.Text;
                if (name == MainMethodName_RhinoScript || name == MainMethodName_GrasshopperScript)
                {
                    // 获取方法体
                    BlockSyntax? body = methodDeclaration.Body;
                    // 检查方法体是否存在
                    if (body != null)
                    {
                        // 获取方法体的大括号
                        // 获取大括号的位置
                        // 创建TextSpan表示方法体的内容
                        methodBodySpan = TextSpan.FromBounds(body.OpenBraceToken.Span.End, body.CloseBraceToken.Span.Start);
                        mainMethodName = name == MainMethodName_RhinoScript ? MainMethodType.Main : MainMethodType.SolveInstance;
                        return mainMethodName;
                    }
                }
            }
            methodBodySpan = member.Span;
            return mainMethodName;
        }


        static MainClassType IsMainClass(MemberDeclarationSyntax member, out ClassDeclarationSyntax? classDeclarationResult)
        {

            if (member is ClassDeclarationSyntax classDeclaration)
            {
                classDeclarationResult = classDeclaration;
                string? mainClassName = classDeclaration.BaseList?.Types.FirstOrDefault()?.Type.ToString();
                if (mainClassName == Script_InstanceName_GrasshopperScript)
                {
                    return MainClassType.GrasshopperInstance;
                }
                if (mainClassName == Script_InstanceName_RhinoScript)
                {
                    return MainClassType.RhinoInstance;
                }
            }
            classDeclarationResult = default;
            return MainClassType.None;
        }
        static void AddLinesFromSourceText(this List<string> strings, TextSpan textSpan, SourceText sourceText)
        {
            if (textSpan.End > textSpan.Start)
            {
                SourceText subText = sourceText.GetSubText(textSpan);
                var lines = subText.Lines.Select(x => x.ToString())
                    .SkipWhile(x => string.IsNullOrWhiteSpace(x))
                    .Reverse()
                    .SkipWhile(x => string.IsNullOrWhiteSpace(x))
                    .Reverse();
                strings.AddRange(lines);
            }
        }
        enum MainClassType
        {
            None = 0,
            RhinoInstance = 1,
            GrasshopperInstance = 2,
        }
        enum MainMethodType
        {
            None = 0,
            Main = 1,
            SolveInstance = 2,
        }

        //static bool IsPublic(this MemberDeclarationSyntax memberDeclaration)
        //{
        //    return memberDeclaration.Modifiers.Any(x => x.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.PublicKeyword));
        //}
        //static bool IsOverride(this MemberDeclarationSyntax memberDeclaration)
        //{
        //    return memberDeclaration.Modifiers.Any(x => x.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.OverrideKeyword));
        //}
    }
}
